/**
Form5
=====

:class:`Form5` is the new HTML5 standard way for creating forms in LiWE.

By using the ``liwe.form5`` static class you can create as many :class:`form5` instance as you want, using the
:func:`liwe.form5.instance()` function.

Bindable events
---------------

Here there is the list of bindable events with ``addEventListener()``.

+---------------+---------------------------------------------------------+
| Name          | Description                                             |
+===============+=========================================================+
|``check-error``| The form did not pass the ``check()`` function          |
+---------------+---------------------------------------------------------+
|``complete``   | The form has been **successfully** submitted completely |
+---------------+---------------------------------------------------------+
|``error``      | The form submission had errors                          |
+---------------+---------------------------------------------------------+
|``progress``   | Updating event during form post                         |
+---------------+---------------------------------------------------------+
|``submit``     | Fired when the user clicks on the ``submit`` button     |
+---------------+---------------------------------------------------------+
|``render``     | The form is being rendered on screen                    |
+---------------+---------------------------------------------------------+
|``reset``      | Fired when the user clicks on the ``reset`` button      |
+---------------+---------------------------------------------------------+




**/

liwe.form5 = {};

liwe.form5._plugins = dict ();
liwe.form5._private_keys = list ( [ "mandatory", "render", "nonl", "filter", "group", "options", "template", "label" ] );
liwe.form5._instances = {};

/**

.. function:: liwe.form5.instance ()

	This static function creates a new :class:`liwe.form5` instance.

**/
liwe.form5.instance = function ( name, attrs )
{
/**
.. class:: liwe.form5
**/
	function form5 ( name, attrs )
	{
		if ( ! attrs ) attrs = {}; 
		this.attrs = attrs;
		this.name = name;
		this.template = null;

		this.attrs = liwe.types.to_dict ( attrs );
		this.attrs [ '_id' ] = name;
		this.attrs [ 'name' ] = name;

		if ( ! this.attrs [ 'class' ] ) this.attrs [ 'class' ] = "liwe-form5";
		if ( this.attrs [ 'role' ] ) 
			this.attrs [ 'role' ] = 'role="' + this.attrs [ 'role' ] + '"';
		else
			this.attrs [ 'role' ] = '';

		this.url = this.attrs.get ( "url", liwe.AJAX.url );

		this._template = null;
		this.templates = dict ( liwe.form5.templates ).clone ();

		this._dest_div = null;
		this._groups = null;

		this.em = new liwe.event_manager ( this );

		this.em.valid_events ( [
			"submit", 		// Events fired when the user clicks on "submit" button
			"reset",		// user clicks on "reset"
			"render",		// The form is being rendered on screen
			"complete",		// The form has been *successfully* submitted completely
			"progress", 		// Updating event during form post
			"error",		// The form submit had errors
			"check-error"		// The form check() failed
		] );

		this.clear (); // Set items, items_by_id, by_name and mandatories

		// This pre-submit event is used for checking mandatories and validation fields
		this.addEventListener ( "submit", this._on_pre_submit, true, true );
		// Added the "submit" system event
		this.addEventListener ( "submit", this._on_submit, true );
		this.addEventListener ( "error", this._on_error, true );
		this.addEventListener ( "check-error", this._on_check_error, true );
	};

	/**
	.. method:: form5.clear ()

		Clear the form and all its fields. 
		This does not clear events
 	**/
	form5.prototype.clear = function ()
	{
		this._items 	    = list ();
		this._items_by_id   = dict ();
		this._items_by_name = dict ();
		this._mandatories   = list ();  // List of mandatory fields in form
		this._validators    = list ();

		this.action = null;
	};

	// {{{ check ()
	/**
	.. method:: form5.check ()

		Checks if the form has been correctly compiled by the user, verifying
		all mandatory fields and checking each field against its validator.

		:param form_id: the unique form id.

		:rtype: ``true`` if the form was correctly compiled, ``false`` if it was not.
	**/
	form5.prototype.check = function ()
	{
		var v;
		var is_valid = true;
		var self = this;
		var errs = { "mandatory" : list (), "validate" : list () };

		this._clear_indicators ();

		this._mandatories.iterate ( function ( item )
		{
			if ( item [ 'ignore' ] ) return;

			v = self._get_value ( item );
			if ( typeof ( v ) == 'undefined' || v.length == 0 ) 
			{
				is_valid = false;
				self.mandatory_error ( item );
		
				errs [ 'mandatory' ].push ( item );

				return;
			}

			self.mandatory_ok ( item );
		} );

		this._validators.iterate ( function ( item )
		{
			if ( item [ 'ignore' ] ) return;

			v = self._get_value ( item );

			if ( typeof ( v ) == 'undefined' || v.length == 0 ) return;

			if ( item.validator ( v ) == false ) 
			{
				is_valid = false;
				self.validation_error ( item );
				errs [ 'validate' ].push ( item );
				return;
			}

			self.validation_ok ( item );
		} );

		errs [ 'is_valid' ] = is_valid;

		return errs;
	};
	// }}}

	form5.prototype.get_values = function ()
	{
		var res = dict ();
		var self = this;

		this._items.iterate ( function ( item, pos )
		{
			if ( item [ 'ignore' ] ) return;
			var v = self._get_value ( item );
			if ( v !== null )
				res [ item [ 'name' ] ] = v;
		} );

		return res;
	};

	form5.prototype.add_multi = function ( lst )
	{
		var self = this;

		lst = list ( lst );

		lst.iterate ( function ( item )
		{
			self.add ( item [ 0 ], item [ 1 ], item [ 2 ] );
		} );
	};

	form5.prototype.get_value = function ( name )
	{
		var item = this.get_item_by_name ( name );
		if ( item [ 'ignore' ] ) return null;
		
		return this._get_value ( item );
	};

	form5.prototype.get_id = function ( name )
	{
		var item = this.get_item_by_name ( name );

		if ( ! item ) return "";

		return item [ '_id' ];
	};

	form5.prototype.set_value = function ( name, v )
	{
		var item = this.get_item_by_name ( name );

		return this._set_value ( item, v );
	};

	form5.prototype.set_focus = function ( name )
	{
		var item = this.get_item_by_name ( name );
		var el = $( item [ '_id' ] );

		el.focus ();
	};

	form5.prototype.set_checked = function ( name, v )
	{
		var item = this.get_item_by_name ( name );

		var dom_item = $( item [ '_id' ] );
		dom_item.checked = !! v;
		if ( v )
			dom_item.setAttribute ( "checked", "checked" );
		else
			dom_item.removeAttribute ( "checked" );
	};

	form5.prototype.set_options = function ( name, opts )
	{
		var item = this.get_item_by_name ( name );

		var dom_item = $( item [ '_id' ] ), options = dom_item.options;
		while ( options.length ) options [ options.length - 1 ] = null;

		var idx = 0, selidx = 0;

		opts = list ( opts );
		opts.iterate ( function ( o )
		{
			var v = o [ 'value' ], opt;
			if ( v && typeof ( v ) != "string" && v.length )
			{
				opt = document.createElement ( 'optgroup' );
				opt.label = o [ 'label' ];
				var lst = list ( v );
				lst.iterate ( function ( o2 )
				{
					var el = document.createElement ( 'option' );
					el.value = o2 [ 'value' ];
					el.innerHTML = o2 [ 'label' ];
					if ( item [ 'value' ] == o2 [ 'value' ] )
						selidx = idx;
					opt.appendChild ( el );
					idx ++;
				} );
			} else {

				opt = document.createElement ( 'option' );
				opt.value = v;
				opt.innerHTML = o [ 'label' ];
				if ( item [ 'value' ] == v )
					selidx = idx;
				idx ++;
			}
			dom_item.appendChild ( opt );
		} );

		dom_item.selectedIndex = selidx;

		// that stupid IE does NOT support innerHTML options assignment...
		//dom_item.innerHTML = liwe.form5._to_options ( this, opts, item [ 'value' ] );
	};

	form5.prototype.mandatory_error = function ( item )
	{
		this._set_mandatory ( item, "err" );
	};

	form5.prototype.mandatory_clear = function ( item )
	{
		this._set_mandatory ( item, "clear" );
	};

	form5.prototype.mandatory_ok = function ( item )
	{
		this._set_mandatory ( item, "ok" );
	};

	form5.prototype.validation_error = function ( item )
	{
		this._set_validation ( item, "err" );
	};

	form5.prototype.validation_clear = function ( item )
	{
		this._set_validation ( item, "clear" );
	};

	form5.prototype.validation_ok = function ( item )
	{
		this._set_validation ( item, "ok" );
	};

	form5.prototype.get_item_by_id = function ( id_item )
	{
		return this._items_by_id [ id_item ];
	};

	form5.prototype.get_item_by_name = function ( name )
	{
		return this._items_by_name [ name ];
	};

	form5.prototype.reset = function ()
	{
		$( this.name ).reset ();
	};

	form5.prototype.submit = function ()
	{
		$( this.name ).submit ();
	};

	/*
 	 *  set_groups ( grp )
 	 *  
 	 *  grp = [ 
 	 *  	{ label : "Group Label", name: "group-name" },
 	 *  	...
 	 *  ]
 	 */
	form5.prototype.set_groups = function ( grp )
	{
		this._groups = to_list ( grp );
	};

	// {{{ add ( type, name, attrs )
	form5.prototype.add = function ( type, name, attrs )
	{
		attrs = dict ( attrs );

		attrs [ 'type' ] = type;

		var cback = liwe.form5._plugins.get ( type );
		
		if ( cback ) cback ( this, type, name, attrs );

		attrs [ 'name' ] = name;
		attrs [ 'autocapitalize' ] = 'off';

		attrs [ '_id' ] = this.name + "-" + name;
		attrs [ '_form_name' ] = this.name;

		if ( ! attrs [ 'label' ] ) attrs [ 'label' ] = "";

		this._items_by_id   [ attrs [ '_id' ] ] = attrs;
		this._items_by_name [ name ] = attrs;
		this._items.push ( attrs );
	};
	// }}}
	// {{{ del ( name )
	form5.prototype.del = function ( name )
	{
		var del_item = -1;

		this._items.iterate ( function ( v, pos )
		{
			if ( v [ 'name' ] == name ) del_item = pos;
		} );

		if ( del_item != -1 ) 
		{
			delete this._items_by_id [ this._items [ del_item ] [ '_id' ] ];
			this._items.del ( del_item );
		}
	};
	// }}}
	// {{{ set_template ( templ )
	form5.prototype.set_template = function ( templ )
	{
		this._template = templ;
	};
	// }}}
	// {{{ to_string ()
	form5.prototype.to_string = function ()
	{
		var buf = new String.buffer ();
		var self = this;

		var rends = this._pre_render ();

		buf.add ( String.formatDict ( this.templates [ 'form-start' ], this.attrs ) );
		this._group_render ( buf, { name: "__default__", label: null }, rends );

		if ( this._groups )
		{
			this._groups.iterate ( function ( grp )
			{
				self._group_render ( buf, grp, rends );
			} );
		}

		buf.add ( String.formatDict ( this.templates [ 'form-end' ], this.attrs ) );

		return buf.toString ();
	};
	// }}}

	form5.prototype.set = function ( dest_div, tpl )
	{
		if ( ! dest_div ) dest_div = this._dest_div;
		this._dest_div = $el ( dest_div );

		if ( ! tpl ) tpl = this.template;

		var html;
		if ( tpl )
		{
			var name, dct = {}, rends = this._pre_render ();
			dct [ 'form-start' ] = String.formatDict ( this.templates [ 'form-start' ], this.attrs );
			dct [ 'form-end' ] = String.formatDict ( this.templates [ 'form-end' ], this.attrs );
			this._items.iterate ( function ( item )
			{
				name = item [ 'name' ];
				dct [ name ] = rends [ name ] [ 'item' ];
				dct [ name + '-label' ] = rends [ name ] [ 'label' ];
			} );

			html = String.formatDict ( tpl, dct );
		} else {
			html = this.to_string ();
		}

		$( this._dest_div, html );

		this._clear_indicators ();

		this.dispatchEvent ( "render", null );
	};

	/* ===================================
 	   INTERNAL FUNCTIONS
	==================================== */
	form5.prototype._clear_indicators = function ()
	{
		var self = this;

		this._mandatories.iterate ( function ( item )
		{
			self.mandatory_clear ( item );
		} );

		this._validators.iterate ( function ( item )
		{
			self.validation_clear ( item );
		} );
	};

	form5.prototype._pre_render = function ()
	{
		var rends = dict ();
		var self = this;

		this._mandatories = list ();

		this._items.iterate ( function ( item )
		{
			if ( ! item [ 'group' ] ) item [ 'group' ] = '__default__';

			if ( item.get ( "mandatory" ) ) 
			{
				self._mandatories.push ( item );
				item [ '_mandatory' ] = true;
				item [ 'required' ] = 'required';
			}

			if ( item.get ( "validator" ) ) self._validators.push ( item );

			// rends [ item [ 'name' ] ] = item.get ( 'render', liwe.form5.render_item ) ( self, item );
			rends [ item [ 'name' ] ] = liwe.form5.render_item ( self, item ); 
		} );

		return rends;
	};

	form5.prototype._group_render = function ( buf, grp, rends )
	{
		var self = this;
		var name = grp [ 'name' ], rend;

		if ( grp [ 'label' ] )
		{
			buf.add ( String.formatDict ( this.templates [ 'group-start' ], grp ) );
			buf.add ( String.formatDict ( this.templates [ 'group-label' ], grp ) );
		}

		this._items.iterate ( function ( item )
		{
			if ( item [ 'group' ] != name ) return;
			
			rend = rends [ item [ 'name' ] ];

			if ( item [ 'label' ] ) buf.add ( rend [ 'label' ] );
			buf.add ( rend [ 'item' ] );

			if ( ! item.get ( "nonl" ) && item [ 'type' ] != 'hidden' ) buf.add ( self.templates [ 'nl' ] );
		} );

		if ( grp [ 'label' ] ) buf.add ( String.formatDict ( this.templates [ 'group-end' ], grp ) );
	};

	// }}}


	form5.prototype._on_pre_submit = function ( evt )
	{
		var errs = this.check ();

		if ( ! errs [ 'is_valid' ] )
		{
			this.dispatchEvent ( "check-error", null, errs );
			return false;
		}

		return true;
	};

	form5.prototype._on_check_error = function ( evt )
	{
		alert ( "Check error" );
	};

	form5.prototype._on_submit = function ( evt )
	{
		if ( ! ( 'FormData' in window ) )
		{
			return this._on_submit_fallback ( evt );
		}
		var form_data = new FormData ();
		var vals = dict ( this.get_values () );
		var self = this;

		if ( this.action ) form_data.append ( "action", this.action );

		vals.iterate ( function ( v, k )
		{
			if ( v && ( typeof ( v ) == 'object' ) && ( v.length ) )
			{
				for ( var i = 0; i < v.length; i ++ )
					form_data.append ( k, v [ i ] );
			}
			else
				form_data.append ( k, v );
		} );
	
		var req = new XMLHttpRequest ();

		req.addEventListener ( 'load',  function ( e ) { return self._on_submit_load  ( e ); }, false );
		req.addEventListener ( 'error', function ( e ) { return self._on_submit_error ( e ); }, false );
		req.addEventListener ( 'abort', function ( e ) { return self._on_submit_error ( e ); }, false );
		
		req.open ( "POST", this.url, true );
		req.upload.addEventListener ( 'progress', function ( e ) { return self._on_submit_progress ( e ); }, false );
		req.send ( form_data );
		
		return false;
	};

	form5.prototype._on_submit_fallback = function ( evt )
	{
		// fallback for *sick* browser ( like IE )' );

		var self = this, vals = dict ( this.get_values () );
		if ( this.action ) vals [ "action" ] = this.action;

		var obj = { "source" : evt.target, "form" : this };

		liwe.AJAX.easy ( vals, function ( response ) 
		{
			obj [ "response" ] = response;
			self.dispatchEvent ( "complete", evt.target, obj );
		}, function ( response, err_descr, err_code, ajax_obj )
		{
			obj [ "response" ] = response;
			self.dispatchEvent ( "error", evt.target, obj );
		} );

		return false;
	};

	form5.prototype._on_error = function ( e )
	{
		var ajax_response = { err_code: -1, err_descr: 'An error occurred.' };

		try
		{
			eval ( e.params.response );
		} catch ( ex ) {
			console.error ( "FORM5.ERROR: %o", e );
		}

		alert ( ajax_response [ 'err_descr' ] );

	};

	form5.prototype._on_submit_progress = function ( e )
	{
		this.dispatchEvent ( "progress", e );
	};

	form5.prototype._on_submit_load = function ( e )
	{
		var self = this;
		var obj = { "source" : e.target, "form" : self };
		var tgt = e.target;

		AJAXManager.handle_easy ( tgt.responseText, 
			function ( res ) { obj [ 'response' ] = res; self.dispatchEvent ( "complete", tgt, obj ); }, 
			function ( res ) { obj [ 'response' ] = res; self.dispatchEvent ( "error", tgt, obj ); }
		);
	};

	form5.prototype._on_submit_error = function ()
	{
		console.debug ( "FORM5.ERROR: %o", arguments );
	};

	form5.prototype._set_mandatory = function ( item, templ_name )
	{
		liwe.form5.set_mandatory ( item, templ_name );
	};

	form5.prototype._set_validation = function ( item, templ_name )
	{
		liwe.form5.set_validation ( item, templ_name );
	};


	form5.prototype._get_value = function ( item )
	{
		return item.get ( 'get_value', function () { return $v ( item [ '_id' ] ); } ) ();
	};

	form5.prototype._set_value = function ( item, v )
	{
		return item.get ( 'set_value', function () 
		{ 
			var it = $( item [ '_id' ] );
			if ( ! it ) return v;

			return it.value = v; 
		} ) ( v );
	};


	var f = new form5 ( name, attrs );

	liwe.form5._instances [ name ] = f;

	return f;
};

liwe.form5.plugin_add = function ( type, create_cback )
{
	liwe.form5._plugins [ type ] = create_cback;
};

liwe.form5.render_item = function ( form, item )
{
	if ( item.get ( "pre-render" ) ) item [ 'pre-render' ] ( form, item );

	var attrs = new String.buffer ();
	var template = form.templates [ item.get ( "template", "input" ) ];
	var res, exit;

	switch ( item.get ( "type" ) )
	{
		case "submit":
		case "reset":
			template = form.templates [ item.get ( "type" ) ];
			break;
	};

	if ( item [ '_mandatory' ] ) template += form.templates [ 'mandatory' ];
	if ( item [ 'validator' ] ) template += form.templates [ 'validation' ];

	if ( typeof ( item [ "value" ] ) == 'undefined' ) item [ 'value' ] = "";

	if ( ! item [ "checked" ] ) delete item [ 'checked' ];
	
	item.iterate ( function ( v, k )
	{
		if ( k.charAt ( 0 ) == '_' ) return;
		try {
			if ( liwe.form5._private_keys.indexOf ( k ) != -1 ) return;
		} catch ( err ) {
			//just for IE8 compatibility
			exit = false;
			liwe.form5._private_keys.iterate ( function ( val ) { if ( val == k ) exit = true; } );
			if ( exit ) return;
		}

		if ( k == 'value' ) 
		{
			v = String ( v ).replace ( /"/g, '&quot;' );
			item [ '_value' ] = v;
		} else 
			attrs.add ( k + '="' + v + '"' );
	} );

	if ( item.get ( "filter" ) ) attrs.add ( 'onkeypress="' + String.formatDict ( form.templates [ 'filter' ], item ) + '"' );

	item [ '_attrs' ] = attrs.get ( " " );
	if ( item.get ( "render" ) )
		res = item [ 'render' ] ( form, item );
	else
 		res = String.formatDict ( template, item );

	if ( typeof ( res ) === 'string' )
		res = { item : res, label : String.formatDict ( form.templates [ 'label' ], item ) };

	return res;
};

liwe.form5.get = function ( form_name )
{
	return liwe.form5._instances [ form_name ];
};

liwe.form5._evt_keypress = function ( form_name, id_item, event )
{
	event = event || window.event;

	var form = liwe.form5.get ( form_name );
	var item = form.get_item_by_id ( id_item );

	if ( ! item.get ( "filter" ) ) return true;

	return filter_valid_chars ( event, item.get ( "filter" ), true );
};

liwe.form5.on_event = function ( event_name, event, dom_form )
{
	event = event || window.event;

	var frm = liwe.form5.get ( dom_form.id );

	return frm.dispatchEvent ( event_name, event, { "source" : dom_form, "form" : frm } );
};

liwe.form5.replace = function ( dest_id )
{
	var el = $( dest_id );

	var name = 'form5-' + el.id;
	if ( $( name ) )
		console.error ( 'FORM5.REPLACE: element with id %s already exists!', name );

	var frm = new liwe.form5.instance ( name );

	var form = el.getElementsByTagName ( 'form' );
	if ( ! form.length ) return null;

	form = form [ 0 ];
	form.onsubmit = function ( event ) { return liwe.form5.on_event ( 'submit', event, this ); };
	form.onreset  = function ( event ) { return liwe.form5.on_event ( 'reset', event, this ); };
	form.name = name;
	form.id = name;

	var i, j, tags = list ( [ 'input', 'textarea', 'select' ] );
	tags.iterate ( function ( tag )
	{
		var item, items = el.getElementsByTagName ( tag );
		for ( i = 0; i < items.length; i++ )
		{
			item = items [ i ];
			if ( tag == 'input' && ( item.type == 'submit' || item.type == 'reset' ) ) continue;
			if ( ! item.name ) console.error ( "FORM5.REPLACE: missing name for %o", item );
			switch ( tag )
			{
				case 'select':
					var o = item.options, opts = [];
					for ( j = 0; j < item.options.length; j++ )
						opts.push ( { label: o [ j ].label, value: o [ j ].value } );
					frm.add ( tag, item.name, { options: opts } );
					break;

				case 'input':
					frm.add ( item.type, item.name );
					break;

				case 'textarea':
					frm.add ( tag, item.name );
					break;

				default:
					console.error ( 'FORM5.REPLACE: unknow tag: %s', tag );
			}
			var frmitem = frm.get_item_by_name ( item.name );
			var frmid = frmitem [ '_id' ];
			if ( $( frmitem [ '_id' ] ) )
				console.error ( 'FORM5.REPLACE: form element with id %s already exists!', frmid );

			item.id = frmid;
		}
	} );

	return frm;
};

liwe.form5.from_model = function ( name, attrs, model )
{
	var fields = list ( model [ 'fields' ] );

	var f = new liwe.form5.instance ( name, attrs );
	fields.iterate ( function ( fld )
	{
		var name = fld [ 'name' ];
		var type = fld [ 'type' ];
		delete fld [ 'type' ];
		delete fld [ 'name' ];
		f.add ( type, name, fld );
	} );

	f.template = model [ 'template' ];

	return f;
};

liwe.form5.set_mandatory = function ( item, templ_name )
{
	var e = $( item [ '_id' ] + '-mandatory' );

	if ( ! e )
	{
		console.warn ( "Could not find element: %s", item [ '_id' ] );
		return;
	}

	e.innerHTML = String.formatDict ( this.templates [ 'mandatory-' + templ_name ], item );
	liwe.dom.del_class ( e, "ok err clear" );
	liwe.dom.add_class ( e, templ_name );
};

liwe.form5.set_validation = function ( item, templ_name )
{
	var e = $( item [ '_id' ] + '-validation' );

	if ( ! e )
	{
		console.warn ( "Could not find element: %s", item [ '_id' ] );
		return;
	}

	e.innerHTML = String.formatDict ( this.templates [ 'validation-' + templ_name ], item );
	liwe.dom.del_class ( e, "ok err clear" );
	liwe.dom.add_class ( e, templ_name );
};

liwe.form5.templates = {
	'form-start' : '<form id="%(_id)s" name="%(name)s" class="%(class)s" %(role)s onsubmit="return liwe.form5.on_event ( \'submit\', event,this)" onreset="return liwe.form5.on_event ( \'reset\', event,this)">',
	'form-end' : '</form>',
	'label' : '<label for="%(_id)s">%(label)s</label>',
	'input' : '<input id="%(_id)s" %(_attrs)s value="%(_value)s"/>',
	'submit' : '<input id="%(_id)s" %(_attrs)s value="%(_value)s"/>',
	'reset' : '<input id="%(_id)s" %(_attrs)s value="%(_value)s"/>',
	'mandatory' : '<span id="%(_id)s-mandatory" class="mandatory"></span>',
	'mandatory-clear' : 'O',
	'mandatory-err' : 'X',
	'mandatory-ok' : 'V',
	'validation' : '<span id="%(_id)s-validation" class="validation"></span>',
	'validation-clear' : '1',
	'validation-err' : '2',
	'validation-ok' : '3',
	'nl' : '<br />',
	'filter' : 'return liwe.form5._evt_keypress(\'%(_form_name)s\',\'%(_id)s\',event)',
	'group-start' : '<fieldset>',
	'group-label' : '<legend>%(label)s</legend>',
	'group-end'   : '</fieldset>'
};


/* 
 * Standard Plugins
 */

// {{{ checkbox ( frm, type, name, attrs ) - Form checkboxes
liwe.form5.plugin_add ( 'checkbox', function ( frm, type, name, attrs )
{
	attrs [ 'get_value' ] = function ()
	{
		var item = $( attrs [ '_id' ] );
		if ( ! item || ! item.checked ) return null;
		return item.value;
	};
} );
// }}}
// {{{ email () - Email input box
liwe.form5.plugin_add ( 'email', function ( frm, type, name, attrs )
{
	attrs [ 'filter' ] = 'abcdefghijklmnopqrstuvwxyz0123456789@._-';
	attrs [ 'validator' ] = liwe.validators.is_email;
} );
// }}}
// {{{ number () - Integer input box
liwe.form5.plugin_add ( 'number', function ( frm, type, name, attrs )
{
	attrs [ 'filter' ] = '0123456789';
} );
// }}}
// {{{ float () - Float input box
liwe.form5.plugin_add ( 'float', function ( frm, type, name, attrs )
{
	attrs [ 'filter' ] = '0123456789.';
} );
// }}}
// {{{ money () - Money input box
liwe.form5.plugin_add ( 'money', function ( frm, type, name, attrs )
{
	attrs [ 'filter' ] = '0123456789.,';
} );
// }}}
// {{{ url () - URL input box
liwe.form5.plugin_add ( 'url', function ( frm, type, name, attrs )
{
	attrs [ 'filter' ] = 'abcdefghijklmnopqrstuvwxyz0123456789._-:/';
} );
// }}}
// {{{ textarea () - Textarea
liwe.form5.templates [ 'textarea' ] = '<textarea id="%(_id)s" %(_attrs)s />%(_value)s</textarea>';

liwe.form5.plugin_add ( 'textarea', function ( frm, type, name, attrs )
{
	attrs [ 'template' ] = 'textarea';
} );
// }}}
// {{{ select - Select field
liwe.form5.templates [ 'select-start' ] = '<select id="%(_id)s" %(_attrs)s>';
liwe.form5.templates [ 'select-end' ] = '</select>';
liwe.form5.templates [ 'select-optgroup-start' ] = '<optgroup label="%(label)s">';
liwe.form5.templates [ 'select-optgroup-end' ] = '</optgroup>';
liwe.form5.templates [ 'select-option'  ] = '<option value="%(value)s" %(_selected)s>%(label)s</option>';

liwe.form5._to_options = function ( form, options, value, attrs )
{
	attrs = attrs || {};

	function _set_selected ( el, value )
	{
		if ( el [ 'value' ] == value )
			el [ '_selected' ] = 'selected="selected"';
		else
			el [ '_selected' ] = '';
	};

	var s = new String.buffer ();
	var k, v, value = value || '';

	to_list ( options );

	if ( attrs [ 'force_select' ] )
		s.add ( String.formatDict ( form.templates [ 'select-option' ], { label: '(Selezionare)', value: '' } ) );

	options.iterate ( function ( el )
	{
		k = el [ 'label' ];
		v = el [ 'value' ];

		if ( v && typeof ( v ) != "string" && v.length )
		{
			s.add ( String.formatDict ( form.templates [ 'select-optgroup-start' ], { label: k } ) );
			to_list ( v );

			v.iterate ( function ( opt )
			{
				_set_selected ( opt, value );
				s.add ( String.formatDict ( form.templates [ 'select-option' ], opt ) );
			} );

			s.add ( form.templates [ 'select-optgroup-end' ] );
		} else {
			_set_selected ( el, value );
			s.add ( String.formatDict ( form.templates [ 'select-option' ], el ) );
		}
	} );

	return s.toString ();
};

liwe.form5.plugin_add ( 'select', function ( frm, type, name, attrs )
{
	attrs [ 'template' ] = 'select';
	attrs [ 'render' ] = function ( form, item )
	{
		var s = new String.buffer ();
		var options = item.get ( "options", [] );
		var value = item.get ( 'value', '' );
		var template = form.templates [ 'select-end' ];

		if ( item [ '_mandatory' ] ) template += form.templates [ 'mandatory' ];
		if ( item [ 'validator' ] ) template += form.templates [ 'validation' ];

		s.add ( String.formatDict ( form.templates [ 'select-start' ], item ) );
		s.add ( liwe.form5._to_options ( form, options, value, attrs ) );
		s.add ( String.formatDict ( template, item ) );

		return s.toString ();
	};
} );
// }}}
// {{{ file - File upload
liwe.form5.plugin_add ( 'file', function ( frm, type, name, attrs )
{
	attrs [ 'get_value' ] = function ()
	{
		var files = $( attrs [ '_id' ] ).files;

		if ( files.length == 0 ) return null;

		if ( attrs [ 'multiple' ] )
		{
			var values = [];
			for ( var i = 0; i < files.length; i ++ )
			{
				values.push ( files.item ( i ) );
			}
			return values;
		} else {
			return files.item ( 0 );
		}
	}
} );
// }}}
// {{{ Progress - Progress box
liwe.form5.templates [ 'progress' ] = '<span>%(label)s</span><div id="%(_id)s-wrapper" class="progress-wrapper"><div id="%(_id)s-bar" class="progress-bar"></div></div>';

liwe.form5.plugin_add ( 'progress', function ( frm, type, name, attrs )
{
	attrs [ 'template' ] = 'progress';

	if ( ! attrs [ 'max-value' ] ) attrs [ 'max-value' ] = 100;

	attrs [ 'max-value' ] = parseInt ( attrs [ 'max-value' ], 10 );

	attrs [ 'set_value' ] = function ( v )
	{
		v = parseInt ( v, 10 );

		var perc = Math.round ( v * 100 / attrs [ 'max-value' ] );

		if ( perc > 100 ) perc = 100;
		if ( perc < 0 )   perc = 0;

		$( attrs [ '_id' ] + "-bar" ).style.width = perc + "%";
	}

	// Event binding
	if ( attrs [ 'form' ] )
	{
		frm.addEventListener ( "progress", function ( e )
		{
			attrs [ 'max-value' ] = e.source.total;

			frm.set_value ( name, e.source.loaded );
		} );
	}
} );
// }}}
// {{{ workspace - Static div workspace
liwe.form5.templates [ 'workspace' ] = '<div id="%(_id)s"></div>';

liwe.form5.plugin_add ( 'workspace', function ( frm, type, name, attrs )
{
	attrs [ 'template' ] = 'workspace';

	attrs [ 'get_value' ] = function ( v ) { return null; };

	attrs [ 'set_value' ] = function ( v )
	{
		$( attrs [ '_id' ], v );
	}
} );
// }}}
// {{{ cod_fisc () - Italian Codice Fiscale
liwe.form5.plugin_add ( 'cod_fisc', function ( frm, type, name, attrs )
{
	attrs [ 'type' ] = 'text';
	attrs [ 'size' ] = 16;
	attrs [ 'maxlength' ] = 16;
	attrs [ 'filter' ] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
	attrs [ 'validator' ] = liwe.validators.is_codice_fiscale;
} );
// }}}
// {{{ p_iva () - Italian VAT code
liwe.form5.plugin_add ( 'p_iva', function ( frm, type, name, attrs )
{
	attrs [ 'type' ] = 'text';
	attrs [ 'size' ] = 11;
	attrs [ 'maxlength' ] = 11;
	attrs [ 'filter' ] = "0123456789";
	attrs [ 'validator' ] = liwe.validators.is_partita_iva;
} );
// }}}
